/* EasyWay Game Engine
 * Copyright (C) 2006 Daniele Paggi.
 *  
 * Written by: 2006 Daniele Paggi<dshnt@hotmail.com>
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.easyway.objects;

import java.io.Serializable;
import java.util.ArrayList;

import org.easyway.annotations.editor.Info;
import org.easyway.annotations.editor.Method;
import org.easyway.input.Mouse;
import org.easyway.interfaces.base.IBaseObject;
import org.easyway.interfaces.base.IDestroyable;
import org.easyway.interfaces.base.IList;
import org.easyway.interfaces.base.IType;
import org.easyway.interfaces.extended.IDrawing;
import org.easyway.interfaces.extended.ILoopable;
import org.easyway.interfaces.extended.IRender;
import org.easyway.interfaces.sprites.IClickable;
import org.easyway.lists.DrawingLayaredList;
import org.easyway.lists.linked.Entry;
import org.easyway.system.StaticRef;

/**
 * Generally all the classes in the Game engine are extended by this class.<br>
 * a BaseObject object has the capacity to:<br> - record his history: where
 * it's used and referenced<br> - auto-adds it self to Game Engine apparates
 * (for example render lists)<br> - auto-removes it self from Game Engine and
 * other references when it's necessary (for example on destroy)
 * 
 * @author Daniele Paggi
 * @version 3
 */
public abstract class BaseObject implements
		IType,
		IDestroyable,
		Serializable,
		IBaseObject {// 0.1.9
	
	private static final long serialVersionUID = 1854678327275417660L;

	/** indicates if the object is destroyed or not */
	private boolean destroyed = false;

	/** a list of lists that references this object */
	protected ArrayList<IList> usedInLists;

	/** a list of entry-lists that references this object */
	protected ArrayList<Entry<IBaseObject>> usedInLikedList;

	/** type of object */
	protected Type type = new Type("$");// 0.2.2.0

	// protected String type = "$"; // 0.1.9 // 0.1.6

	/**
	 * if setted to true when this object is created it'll auto-added to game
	 * engine apparates
	 */
	public static boolean autoAddToLists = true;

	/**
	 * indicates if the object is to kill or destroy<br>
	 * If the object is to kill will not executed the onDestroy method.
	 */
	public static boolean KILL = false;

	/**
	 * indicates if the class has the method:<br>
	 * public void loop()
	 * 
	 * @see ILoopable
	 */
	public boolean isLoopable;

	/**
	 * indicates if the class has the method:<br>
	 * public void render()
	 * 
	 * @see IRender
	 */
	public boolean isDrawable;

	/**
	 * indicates if the class impelemnts the IClickable interface;
	 * 
	 * @see IClickable;
	 */
	public boolean isClickable;

	/**
	 * the game engine has, for default, 7 layers (drawing-sheet).<br>
	 * the idLayer indicates in witch layer this object will be added to.<br>
	 * <br>
	 * <b>example:</b><br> ..<br>
	 * BaseObject ob = new BaseObject();<br>
	 * ob.setIdLayer( 1 ); // MOVE to layer n.1<br> ..<br>
	 * <br>
	 * <b>example 2:</b><br> ..<br>
	 * BaseObject ob = new BaseObject( 1 ); // creates DIRECTLY in layer n.1<br> ..<br>
	 */
	// private int idLayer = 3; // 0.2.2.0// 0.1.9
	/** crates a new instance of object */
	public BaseObject() {
		// 0.2.2.0
		this(autoAddToLists);
	}

	/**
	 * crates a new instance of object
	 * 
	 * @param autoAddToLists
	 *            indicates if it'll auto-added to game engine apparates
	 */
	// 0.2.2.0
	// public BaseObject(boolean autoAddToLists) {
	// this(autoAddToLists, ILayer.defaultIdLayer);
	// }
	/**
	 * crates a new instance of object
	 * 
	 * @param idLayer
	 *            indicates in witch layer (drawing-sheet) this object will be
	 *            added to.
	 */
	// 0.2.2.0
	// public BaseObject(int idLayer) {
	// this(autoAddToLists, idLayer);
	// }
	/**
	 * crates a new instance of object
	 * 
	 * @param autoAddToLists
	 *            indicates if it'll auto-added to game engine apparates
	 * @param idLayer
	 *            indicates in witch layer (drawing-sheet) this object will be
	 *            added to.
	 */
	// 0.2.2.0
	public BaseObject(boolean autoAddToLists) { // , int idLayer) {
		usedInLists = new ArrayList<IList>(10);// new Vector<IList>();
		usedInLikedList = new ArrayList<Entry<IBaseObject>>(10);
		isDrawable = this instanceof IRender;
		isLoopable = this instanceof ILoopable;
		isClickable = this instanceof IClickable;
		addToGELists(autoAddToLists);
	}

	public BaseObject(BaseObject obj) {
		super();
		destroyed = obj.destroyed;
		usedInLists = new ArrayList<IList>(10);
		usedInLikedList = new ArrayList<Entry<IBaseObject>>(10);
		this.type = obj.type;
		this.isLoopable = obj.isLoopable;
		this.isDrawable = obj.isDrawable;
		this.isClickable = obj.isClickable;
		for (IList list : obj.usedInLists) {
			list.add(this);
		}
		// addToLists();
	}

	/**
	 * adds the BaseObject to the game engine's lists
	 * 
	 */
	protected void addToGELists(boolean toAdd) {
		if (!StaticRef.initialized || !autoAddToLists || !toAdd)
			return;
		if (isClickable) {
			Mouse.add((IClickable) this);
		}
		if (isLoopable) {
			StaticRef.loopList.add((ILoopable) this);
		}
	}

	/**
	 * removes and readd itself from all drawing list that contain it<br>
	 * It's used generally when the layer (not IdLayer) change
	 */
	public void readdToDrawingLists() {
		if (!isDrawable)
			return;
		DrawingLayaredList temp;
		for (int i = usedInLists.size() - 1; i >= 0; --i) {
			if (usedInLists.get(i) instanceof DrawingLayaredList) {
				temp = (DrawingLayaredList) usedInLists.get(i);
				temp.remove((IDrawing) this);
				temp.add((IDrawing) this);
			}
		}
	}

	/**
	 * returns the IdLayer (the id of drawing-sheet)<br>
	 * 
	 * @return id of drawing layer on witch the object will be drawen
	 */
	// 0.2.2.0
	// public int getIdLayer() {
	// return idLayer;
	// }
	/**
	 * sets the IdLayer (the id of drawing-sheet)
	 * 
	 * @param id
	 *            the id-layer
	 */
	// 0.2.2.0
	// public void setIdLayer(int id) {
	// if (!isDrawable)
	// return;
	// try {
	// layers[idLayer].remove(this);
	// } catch (Exception e) {
	// Utility.error(" >> LOGIC ERROR <<"
	// + " you can't change the idlayer of "
	// + "an object that has not been auto-added "
	// + "by the game engine to "
	// + "the system drawing list (have you used idLayer "
	// + "instead of layer?)", "BaseObject.setIdLayer(int)", e);
	// }
	// if (id < 0) {
	// id = 0;
	// } else if (id > layers.length) { // 0.1.9
	// id = layers.length-1; // 0.1.9
	// }
	// idLayer = id;
	// layers[idLayer].add(this);
	// // readdToDrawingLists();
	// }
	/**
	 * returns if the object is destroyed or not
	 * 
	 * @return the object is destroyed?
	 */
	public boolean isDestroyed() {
		return destroyed;
	}

	// /** note: ricorda di richiamare super.destroy()!!!! */
	/**
	 * destroys the object<br>
	 * <b>Note:</b> when You extends a class from this remember to call
	 * "super.destroy()"!<br>
	 * for example:<br>
	 * <br>
	 * <font color=GREEN>public class</font> MyClass <font color=GREEN>extends</font>
	 * BaseObject <font color=GREEN>{</font><br>
	 * ...<br>
	 * <font color=GREEN>public void</font> destroy() <font color=GREEN>{</font><br>
	 * if (isDestroyed()) <br>
	 * return;<br>
	 * super.detroy();<br>
	 * ...<br>
	 * <font color=GREEN>}<br> }<br>
	 * </font>
	 * 
	 * @see #kill()
	 */
	public void destroy() {
		if (destroyed)
			return;
		destroyed = true;
		if (!KILL) // 0.1.6
			onDestroy();
		for (int i = usedInLists.size() - 1; i >= 0; i--) {
			usedInLists.get(i).remove(this);
		}
	}

	/**
	 * kill the object.<br>
	 * It's similar to the destroy method but this will not call the onDestroy
	 * method.<br>
	 * 
	 * @see #destroy()
	 */
	@Method("kill the object")//
	public final void kill() {// 0.2.0 // 0.1.9 // 0.1.6
		boolean backupKILL = KILL;
		KILL = true;
		destroy();
		KILL = backupKILL;
	}

	/**
	 * this method is called when the object is destroyed<br>
	 * example:<br>
	 * <font color=GREEN>public class</font> MyClass <font color=GREEN>extends</font>
	 * BaseObject <font color=GREEN>{</font><br>
	 * ...<br>
	 * <font color=GREEN>public void</font> onDestroy() <font color=GREEN>{</font><br>
	 * scores += 1; <font color=GREEN>//increases the score<br> }<br> }</font><br>
	 * 
	 */
	protected void onDestroy() {
	}

	// the kill method remove the object from all the lists that contains this
	// object
	// the finalize method is called when the object isn't reachable and, then,
	// there aren't lists
	// that contain the object..
	// when, we can don't use the finalize method :)
	// public void finalize() {
	// if (isDestroyed())
	// return;
	// kill();
	// }

	// 0.2.2.0 // 0.1.9
	public Type getType() {
		return type;
	}

	@Method("set the Type-name")//
	public void setType(@Info("type name") String type) {
		getType().set(type);
	}

	// 0.2.2.0 // 0.1.9
	// public void setType(String type) {
	// this.type = type;
	// }

	// -----------------------
	// 0.2.2.0
	public ArrayList<IList> getUsedLists() {
		return usedInLists;
	}

	// 0.2.2.0
	public ArrayList<IList> getGraphicLists() {
		return usedInLists;

	}

	// 0.2.2.0
	public ArrayList<IList> getDataLists() {
		return usedInLists;
	}

	// 0.2.2.0
	public ArrayList<IList> getExtraLists() {
		return usedInLists;
	}

	public String toString() {
		return type.toString();
	}

}
